﻿using System;
using System.Collections.Generic;
using System.Linq;
using AutoClub.Core.Dao.Implementations;
using AutoClub.Core.Domain;
using AutoClub.Core.Domain.Dao;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace AutoClub.Core.Tests
{
    /// <summary>
    /// Test Suite for Repo Users Integration test
    /// </summary>
    [TestClass]
    public class UsersRepoTests : BaseRepoTest
    {
        #region Fields

        IUsersRepo myRepoToTest;
        IUserRolesRepo userRolRepo;
        ICarCategoriesRepo carCategoriesRepo;

        #endregion

        [TestInitialize()]
        public override void MyTestInitialize()
        {
            base.MyTestInitialize();

            userRolRepo = new UserRolesRepo(this.uof);
            carCategoriesRepo = new CarCategoriesRepo(this.uof);

            myRepoToTest = new UsersRepo(this.uof, userRolRepo, carCategoriesRepo);
        }

        [TestCleanup()]
        public override void MyTestCleanup()
        {
            base.MyTestCleanup();

        }

        #region Test Methods

        /// <summary>
        /// Tests the Get All method when there is no users in the database
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestGetAllWhenThereIsNoData()
        {
            //Setup

            //SUT
            IEnumerable<User> result = this.myRepoToTest.GetAll();

            //Asserts
            result.Should().NotBeNull("Expected an enumerable of users");
            result.Should().BeEmpty("Expected no elements in the result set");
        }

        /// <summary>
        /// Tests the Add User Method for a single insertion
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestAddUserMethod()
        {
            //Setup
            CarCategory category = this.carCategoriesRepo.Get(CarCategoryEnum.Category3);
            User instance = this.modelHelper.CreateDefaultUser(category);

            //SUT
            User result = this.myRepoToTest.Add(instance.Email, instance.Password, category.Value);
            this.uof.Commit();

            //Asserts
            result.Should().NotBeNull("Expected a new user add it");
            result.ShouldHave().AllPropertiesBut(u => u.Id)
                    .But(u => u.Active)
                    .But(u => u.IsAdminApproved)
                    .But(u => u.CreatedBy)
                    .But(u => u.CreatedOn)
                    .But(u => u.Rol)
                    .EqualTo(instance, "Expected to have all properties persisted");
            result.Id.Should().NotBe(Guid.Empty, "Expected a non empty id");
            result.Active.Should().BeTrue("Expected an active object");
            result.IsAdminApproved.Should().BeFalse("Expected a not active approved object");
        }

        /// <summary>
        /// Tests the Get by Method when there are a few users to retrieve
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestGetByMethod()
        {
            //Setup
            CarCategory category = this.carCategoriesRepo.Get(CarCategoryEnum.Category3);
            User instance1 = this.modelHelper.CreateDefaultUser("leoponzio@gmail.com", category);
            User instance2 = this.modelHelper.CreateDefaultUser("davidtrezeguet@hotmail.com", category);
            User instance3 = this.modelHelper.CreateDefaultUser("elmuertodefunesmori@gmail.com", category);

            instance1 = this.myRepoToTest.Add(instance1.Email, instance1.Password, instance1.RaceCategory.Value);
            instance2 = this.myRepoToTest.Add(instance2.Email, instance2.Password, instance2.RaceCategory.Value);
            instance3 = this.myRepoToTest.Add(instance3.Email, instance3.Password, instance3.RaceCategory.Value);
            this.uof.Commit();

            //SUT
            IEnumerable<User> result = this.myRepoToTest.GetBy(b => b.Email.EndsWith("@gmail.com"));
            result = result.OrderBy(r => r.Email).AsEnumerable();

            //Asserts
            result.Should().NotBeNull("Expected a enumerable of users to retrieve");
            result.Should().NotBeEmpty("Expected 2 users");

            IEnumerator<User> set = result.GetEnumerator();

            set.MoveNext();
            set.Current.ShouldHave().AllProperties().EqualTo(instance3, "Expected to be the second user");
            set.MoveNext();
            set.Current.ShouldHave().AllProperties().EqualTo(instance1, "Expected to be the first user");
        }

        /// <summary>
        /// Tests the Get by Id Method when there is a single user to retrieve
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestGetMethod()
        {
            //Setup
            CarCategory category = this.carCategoriesRepo.Get(CarCategoryEnum.Category3);
            User instance = this.modelHelper.CreateDefaultUser(category);
            instance = this.myRepoToTest.Add(instance.Email, instance.Password, category.Value);
            this.uof.Commit();

            //SUT
            User result = this.myRepoToTest.Get(instance.Id);

            //Asserts
            result.Should().NotBeNull("Expected a user retrieve it");
            result.ShouldHave().AllProperties().EqualTo(instance, "Expected to have all properties persisted");
        }

        /// <summary>
        /// Tests the Update Method when there is a single user to retrieve
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestUpdateMethod()
        {
            //Setup
            CarCategory category = this.carCategoriesRepo.Get(CarCategoryEnum.Category3);
            User instance = this.modelHelper.CreateDefaultUser(category);
            instance = this.myRepoToTest.Add(instance.Email, instance.Password, category.Value);
            this.uof.Commit();

            //SUT
            instance.Password = "newPassword";
            User result = this.myRepoToTest.Update(instance);
            this.uof.Commit();

            //Asserts
            result.Should().NotBeNull("Expected a user retrieve it");
            result.ShouldHave().AllProperties().EqualTo(instance, "Expected to have all properties persisted");
        }

        /// <summary>
        /// Tests the Remove Method when there is a single user to retrieve
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestRemoveMethod()
        {
            //Setup
            CarCategory category = this.carCategoriesRepo.Get(CarCategoryEnum.Category3);
            User instance = this.modelHelper.CreateDefaultUser(category);
            this.myRepoToTest.Add(instance.Email, instance.Password, category.Value);
            this.uof.Commit();

            //SUT
            User result = this.myRepoToTest.Remove(instance);
            this.uof.Commit();

            //Asserts
            result.Should().NotBeNull("Expected a user retrieve it");
            result.ShouldHave().AllProperties().EqualTo(instance, "Expected to have all properties persisted");
            result.Active.Should().BeFalse("Expected an inactive instance");
        }

        /// <summary>
        /// Tests the Get method for a Removed entity, so there is no data to retrieve.
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestGetOfARemoveEntityMethod()
        {
            //Setup
            CarCategory category = this.carCategoriesRepo.Get(CarCategoryEnum.Category3);
            User instance = this.modelHelper.CreateDefaultUser(category);
            this.myRepoToTest.Add(instance.Email, instance.Password, category.Value);
            this.uof.Commit();
            this.myRepoToTest.Remove(instance);
            this.uof.Commit();

            //SUT
            User result = this.myRepoToTest.Get(instance.Id);

            //Asserts
            result.Should().BeNull("Expected a null result because the instance was removed.");
        }

        /// <summary>
        /// Tests the Is Valid User method for a non valid user, so there is no data to retrieve.
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestIsValidUserMethodFalseResult()
        {
            //Setup

            //SUT
            bool result = this.myRepoToTest.IsUserValid("someEmail", "somePassword");

            //Asserts
            result.Should().BeFalse("Expected a false result because the instance does not exist.");
        }


        /// <summary>
        /// Tests the Is Valid User method for a valid user, so there is data to retrieve.
        /// </summary>
        [TestMethod, TestCategory("Integration")]
        public void TestIsValidUserMethodTrueResult()
        {
            //Setup
            CarCategory category = this.carCategoriesRepo.Get(CarCategoryEnum.Category3);
            User instance = this.modelHelper.CreateDefaultUser(category);
            this.myRepoToTest.Add(instance.Email, instance.Password, category.Value);
            this.uof.Commit();

            //SUT
            bool result = this.myRepoToTest.IsUserValid(instance.Email, instance.Password);

            //Asserts
            result.Should().BeTrue("Expected a true result because the instance exists.");
        }

        #endregion
    }
}
